package com.wsoft.bpm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.PageHelper;
import com.wsoft.bpm.bean.ActModelMetaInfo;
import com.wsoft.bpm.dto.ActProcessDTO;
import com.wsoft.bpm.entity.ActWorkflowConfigEntity;
import com.wsoft.bpm.mapper.ActProcessMapper;
import com.wsoft.bpm.service.ActProcessService;
import com.wsoft.bpm.service.IActWorkflowConfigService;
import com.wsoft.bpm.vo.ProcessListVO;
import com.wsoft.constant.CommonConstant;
import com.wsoft.core.exception.Asserts;
import com.wsoft.entity.BaseEntity;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.xml.stream.XMLInputFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @Author zhonglj
 * @Date 2024-08-28 15:20
 */
@Slf4j
@Service
public class ActProcessServiceImpl implements ActProcessService {
    @Resource
    private ActProcessMapper actProcessMapper;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private IActWorkflowConfigService actWorkflowConfigService;
    @Resource
    private RuntimeService runtimeService;

    @Override
    public List<ProcessListVO> list(ActProcessDTO dto) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        return actProcessMapper.list(dto);
    }

    @Override
    public Boolean resetModel(String deploymentId, String fileName) {
        byte[] xmlData = getXmlData(deploymentId, fileName);

        if (xmlData != null) {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
            if (processDefinition != null) {
                // 首先定位模型
                List<Model> models = repositoryService.createModelQuery().list();
                for (Model model : models) {
                    ActModelMetaInfo metaInfo = JSONUtil.toBean(model.getMetaInfo(), ActModelMetaInfo.class);
                    if (CollUtil.contains(metaInfo.getHistoryKeys(), processDefinition.getKey())) {
                        try {
                            // 再更新模型xml数据
                            BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(XMLInputFactory.newInstance()
                                    .createXMLStreamReader(new InputStreamReader(new ByteArrayInputStream(xmlData), StandardCharsets.UTF_8)));
                            ObjectNode modelNode = new BpmnJsonConverter().convertToJson(bpmnModel);
                            repositoryService.addModelEditorSource(model.getId(), modelNode.toString().getBytes(StandardCharsets.UTF_8));

                            // 清除当前模型下的待保存json数据
                            actWorkflowConfigService.remove(
                                    Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                                            .eq(ActWorkflowConfigEntity::getModelId, model.getId())
                                            .eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO)
                                            .isNull(ActWorkflowConfigEntity::getProcDefId)
                            );
                            // 将历史版本的json覆盖当前最新json
                            List<ActWorkflowConfigEntity> targetVersionConfig = actWorkflowConfigService.list(
                                    Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                                            .eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO)
                                            .eq(ActWorkflowConfigEntity::getModelId, model.getId())
                                            .eq(ActWorkflowConfigEntity::getProcDefId, processDefinition.getId())
                            );

                            if (CollUtil.isEmpty(targetVersionConfig)) {
                                Asserts.fail("[bpm.process.error.config.nonexistent]");
                            }

                            targetVersionConfig.stream().forEach(e -> {
                                e.setId(null);
                                e.setProcDefId(null);
                            });

                            actWorkflowConfigService.saveBatch(targetVersionConfig);
                            return true;
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                        }
                    }
                }
            }
        }

        return false;
    }

    @Override
    public String xmlView(String deploymentId, String fileName) {
        byte[] xmlData = getXmlData(deploymentId, fileName);
        if (xmlData != null) {
            return new String(xmlData, StandardCharsets.UTF_8);
        }
        return "";
    }

    @Override
    public void suspend(String processDefinitionId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        if (processDefinition != null) {
            // 查看是否可以挂起
            boolean suspended = processDefinition.isSuspended();
            if (suspended) {
                Asserts.fail("[bpm.process.error.suspend.suspended]");
            }
            repositoryService.suspendProcessDefinitionById(processDefinitionId);
        }

    }

    @Override
    public void active(String processDefinitionId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        if (processDefinition != null) {
            // 查看是否可以激活
            boolean suspended = processDefinition.isSuspended();
            if (!suspended) {
                Asserts.fail("[bpm.process.error.active.activated]");
            }
            repositoryService.activateProcessDefinitionById(processDefinitionId);
        }
    }

    private byte[] getXmlData(String deploymentId, String fileName) {
        List<String> names = repositoryService.getDeploymentResourceNames(deploymentId);
        byte[] xmlData = null;
        for (String name : names) {
            if (StrUtil.endWith(name, ".zip")) {
                continue;
            }
            if (StrUtil.contains(name, fileName)) {
                InputStream in = repositoryService.getResourceAsStream(deploymentId, name);
                xmlData = IoUtil.readBytes(in, true);
                break;
            }
        }

        return xmlData;
    }
}
